import tkinter as tk
from tkinter import ttk
import cv2
import numpy as np
from PIL import Image, ImageTk
import time
from datetime import datetime
import paho.mqtt.client as mqtt
import json
import base64
import threading
from tkinter import messagebox

class TimeDisplayApp:
    def __init__(self, root):
        self.root = root
        self.root.title("图像MQTT发送器")
        
        # 设置窗口最小尺寸
        self.root.minsize(800, 600)
        
        # 更新主题样式，添加更现代的外观
        style = ttk.Style()
        style.configure("TButton", padding=8, font=('微软雅黑', 10))
        style.configure("TLabel", padding=8, font=('微软雅黑', 10))
        style.configure("TEntry", padding=8)
        style.configure("TLabelframe", padding=10)
        style.configure("TLabelframe.Label", font=('微软雅黑', 10, 'bold'))
        
        # 创建主框架并添加内边距
        main_frame = ttk.Frame(root, padding="20")
        main_frame.grid(row=0, column=0, sticky="nsew")
        
        # 创建左侧控制面板
        left_panel = ttk.Frame(main_frame)
        left_panel.grid(row=0, column=0, sticky="ns", padx=(0, 10))
        
        # 控制面板放在左侧
        control_frame = ttk.LabelFrame(left_panel, text="控制面板")
        control_frame.pack(fill="x", pady=(0, 10))
        
        # 间隔时间输入优化布局
        time_frame = ttk.Frame(control_frame)
        time_frame.pack(fill="x", padx=10, pady=5)
        ttk.Label(time_frame, text="刷新间隔(秒):").pack(side="left")
        self.interval_var = tk.StringVar(value="0.5")
        self.interval_entry = ttk.Entry(time_frame, textvariable=self.interval_var, width=10)
        self.interval_entry.pack(side="left", padx=5)
        ttk.Button(time_frame, text="修改", command=self.update_interval).pack(side="left", padx=5)
        
        # MQTT配置优化布局
        mqtt_frame = ttk.LabelFrame(left_panel, text="MQTT配置")
        mqtt_frame.pack(fill="x")
        
        # IP配置行
        ip_frame = ttk.Frame(mqtt_frame)
        ip_frame.pack(fill="x", padx=10, pady=5)
        ttk.Label(ip_frame, text="本机Broker IP:").pack(side="left")
        self.ip_var = tk.StringVar(value="192.168.18.18")
        self.ip_entry = ttk.Entry(ip_frame, textvariable=self.ip_var)
        self.ip_entry.pack(side="left", padx=5, fill="x", expand=True)
        
        # 端口配置行
        port_frame = ttk.Frame(mqtt_frame)
        port_frame.pack(fill="x", padx=10, pady=5)
        ttk.Label(port_frame, text="端口号:").pack(side="left")
        self.port_var = tk.StringVar(value="1883")
        self.port_entry = ttk.Entry(port_frame, textvariable=self.port_var, width=10)
        self.port_entry.pack(side="left", padx=5)
        
        # 主题配置行
        topic_frame = ttk.Frame(mqtt_frame)
        topic_frame.pack(fill="x", padx=10, pady=5)
        ttk.Label(topic_frame, text="主题:").pack(side="left")
        self.topic_var = tk.StringVar(value="/image")
        self.topic_entry = ttk.Entry(topic_frame, textvariable=self.topic_var)
        self.topic_entry.pack(side="left", padx=5, fill="x", expand=True)
        
        # 连接控制按钮行
        btn_frame = ttk.Frame(mqtt_frame)
        btn_frame.pack(fill="x", padx=10, pady=5)
        self.connect_btn = ttk.Button(btn_frame, text="连接", command=self.start_mqtt_connection)
        self.connect_btn.pack(side="left", padx=5)
        self.disconnect_btn = ttk.Button(btn_frame, text="断开", command=self.disconnect_mqtt)
        self.disconnect_btn.pack(side="left", padx=5)
        self.status_label = ttk.Label(btn_frame, text="未连接", foreground="red")
        self.status_label.pack(side="left", padx=5)

        # 图像显示区域放在右侧，并添加边框
        image_frame = ttk.LabelFrame(main_frame, text="图像预览")
        image_frame.grid(row=0, column=1, sticky="nsew")
        
        self.canvas = tk.Canvas(image_frame, bg='black', highlightthickness=0)
        self.canvas.pack(fill="both", expand=True, padx=10, pady=10)

        # 配置grid权重
        root.grid_rowconfigure(0, weight=1)
        root.grid_columnconfigure(0, weight=1)
        main_frame.grid_rowconfigure(0, weight=1)
        main_frame.grid_columnconfigure(1, weight=3)  # 图像区域占更多空间
        
        # 添加线程安全的标志
        self.mqtt_connected = threading.Event()
        self.mqtt_lock = threading.Lock()
        
        # 初始化变量
        self.interval = 0.5
        self.is_running = True
        self.mqtt_client = None
        
        # 添加新的标志来控制运行状态
        self.should_run = threading.Event()
        self.should_run.set()
        
        # 开始更新图像
        self.update_image()
        
    def start_mqtt_connection(self):
        """在后台线程中启动MQTT连接"""
        self.connect_btn.config(state="disabled")
        self.status_label.config(text="正在连接...", foreground="orange")
        threading.Thread(target=self.connect_mqtt, daemon=True).start()
        
    def create_time_image(self):
        # 优化图像生成效率
        img = np.zeros((480, 640, 3), dtype=np.uint8)
        
        # 使用向量化操作绘制网格线
        x_coords = np.arange(0, 640, 30)
        y_coords = np.arange(0, 480, 30)
        
        for x in x_coords:
            img[:, x] = [0, 50, 0]
        for y in y_coords:
            img[y, :] = [0, 50, 0]
        
        # 修改随机点生成的方式
        for _ in range(50):
            y = np.random.randint(0, 480)
            x = np.random.randint(0, 640)
            color = np.random.randint(0, 256)
            cv2.circle(img, (x, y), 2, (int(color), int(color), int(color)), -1)
        
        # 获取当前时间，增加毫秒显示
        current_time = datetime.now().strftime("%H:%M:%S.%f")[:-5]
        
        # 设置时间文本参数
        font = cv2.FONT_HERSHEY_SIMPLEX
        font_scale = 2.0
        thickness = 2
        text_size = cv2.getTextSize(current_time, font, font_scale, thickness)[0]
        
        # 计算时间文本位置使其居中
        text_x = (img.shape[1] - text_size[0]) // 2
        text_y = (img.shape[0] + text_size[1]) // 2
        
        # 在图像上绘制时间
        cv2.putText(img, current_time, (text_x, text_y), font,
                   font_scale, (0, 255, 0), thickness)
        
        # 修改QQ号显示，添加冒号
        qq_text = "QQ:770896174"
        qq_font_scale = 1.0
        qq_thickness = 2
        qq_text_size = cv2.getTextSize(qq_text, font, qq_font_scale, qq_thickness)[0]
        qq_x = (img.shape[1] - qq_text_size[0]) // 2
        qq_y = text_y + 60  # 在时间下方显示
        
        # 绘制QQ号
        cv2.putText(img, qq_text, (qq_x, qq_y), font,
                   qq_font_scale, (0, 255, 255), qq_thickness)
        
        return img
    
    def update_image(self):
        if self.is_running:
            # 创建带时间的图像
            img = self.create_time_image()
            
            # 发送图像到MQTT
            self.send_image_mqtt(img)
            
            # 转换图像格式用于显示
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            img = Image.fromarray(img)
            img = ImageTk.PhotoImage(image=img)
            
            # 更新画布大小
            self.canvas.config(width=img.width(), height=img.height())
            
            # 显示图像
            self.canvas.create_image(img.width()//2, img.height()//2, 
                                   image=img, anchor=tk.CENTER)
            self.canvas.image = img
            
            # 使用更新后的interval值设置下一次更新时间
            self.root.after(int(self.interval * 1000), self.update_image)
    
    def update_interval(self):
        try:
            new_interval = float(self.interval_var.get())
            if new_interval <= 0:
                raise ValueError("间隔时间必须大于0")
            self.interval = new_interval
        except ValueError as e:
            # 显示错误消息
            tk.messagebox.showerror("错误", f"无效的时间间隔: {str(e)}")
            self.interval_var.set(str(self.interval))
    
    def send_image_mqtt(self, img):
        """发送图像到MQTT服务器"""
        if not self.mqtt_connected.is_set() or not self.should_run.is_set():
            return
            
        try:
            with self.mqtt_lock:
                # 设置JPEG压缩参数
                encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90]  # 90%的质量
                
                # 将图像编码为JPEG格式，然后转换为base64字符串
                _, img_encoded = cv2.imencode('.jpg', img, encode_param)
                img_base64 = base64.b64encode(img_encoded.tobytes()).decode('utf-8')
                
                # 直接发送base64编码的图像数据
                self.mqtt_client.publish(self.mqtt_topic, img_base64)
        except Exception as e:
            self.root.after(0, lambda: self.status_label.config(
                text=f"发送失败: {str(e)}", foreground="red"))
    
    def connect_mqtt(self):
        """建立MQTT连接"""
        try:
            # 验证输入
            if not self.ip_var.get().strip():
                raise ValueError("服务器IP不能为空")
            if not self.port_var.get().strip():
                raise ValueError("端口号不能为空")
            if not self.topic_var.get().strip():
                raise ValueError("主题不能为空")
            
            with self.mqtt_lock:
                if self.mqtt_client:
                    self.mqtt_client.loop_stop()
                    self.mqtt_client.disconnect()
                
                self.mqtt_broker = self.ip_var.get().strip()
                self.mqtt_port = int(self.port_var.get())
                self.mqtt_topic = self.topic_var.get().strip()
                
                # 使用新版本的 MQTT Client API
                self.mqtt_client = mqtt.Client(protocol=mqtt.MQTTv5)
                self.mqtt_client.on_connect = self.on_mqtt_connect
                self.mqtt_client.on_disconnect = self.on_mqtt_disconnect
                
                # 设置连接超时
                self.mqtt_client.connect(self.mqtt_broker, self.mqtt_port, keepalive=60)
                self.mqtt_client.loop_start()
                
        except ValueError as ve:
            error_msg = str(ve)
            self.root.after(0, lambda msg=error_msg: [
                self.status_label.config(text=f"输入错误: {msg}", foreground="red"),
                self.connect_btn.config(state="normal")
            ])
        except Exception as ex:
            error_msg = str(ex)
            self.root.after(0, lambda msg=error_msg: [
                self.status_label.config(text=f"连接失败: {msg}", foreground="red"),
                self.connect_btn.config(state="normal")
            ])
    
    def on_mqtt_connect(self, client, userdata, flags, rc, properties=None):
        """MQTT连接回调"""
        if rc == 0:
            self.mqtt_connected.set()
            self.root.after(0, lambda: [
                self.status_label.config(text="已连接", foreground="green"),
                self.connect_btn.config(state="disabled")
            ])
        else:
            self.root.after(0, lambda: [
                self.status_label.config(text=f"连接失败(code:{rc})", foreground="red"),
                self.connect_btn.config(state="normal")
            ])
    
    def on_mqtt_disconnect(self, client, userdata, rc):
        """MQTT断开连接回调"""
        self.mqtt_connected.clear()
        self.root.after(0, lambda: [
            self.status_label.config(text="已断开", foreground="red"),
            self.connect_btn.config(state="normal")
        ])
    
    def disconnect_mqtt(self):
        """断开 MQTT 连接"""
        try:
            # 立即更新UI状态
            self.connect_btn.config(state="normal")
            self.status_label.config(text="正在断开...", foreground="orange")
            
            # 清除标志，立即停止发送消息
            self.should_run.clear()
            self.mqtt_connected.clear()
            
            # 在后台线程中处理断开连接
            def disconnect_thread():
                with self.mqtt_lock:
                    if self.mqtt_client:
                        try:
                            self.mqtt_client.loop_stop()
                            self.mqtt_client.disconnect()
                        except Exception as e:
                            print(f"断开MQTT连接时发生错误: {str(e)}")
                        finally:
                            self.mqtt_client = None
                
                # 更新UI显示断开状态
                self.root.after(0, lambda: self.status_label.config(text="已断开", foreground="red"))
                self.should_run.set()  # 恢复标志位
            
            # 启动后台断开线程
            threading.Thread(target=disconnect_thread, daemon=True).start()
            
        except Exception as e:
            self.status_label.config(text=f"断开失败: {str(e)}", foreground="red")
    
    def __del__(self):
        """析构函数"""
        self.should_run.clear()  # 确保停止所有操作
        if hasattr(self, 'mqtt_client') and self.mqtt_client:
            try:
                self.mqtt_client.loop_stop()
                self.mqtt_client.disconnect()
            except:
                pass

if __name__ == "__main__":
    root = tk.Tk()
    app = TimeDisplayApp(root)
    root.mainloop()
